package com.dbflow5.query.list;

import com.dbflow5.adapter.RetrievalAdapter;
import com.dbflow5.database.DatabaseWrapper;
import com.dbflow5.database.FlowCursor;
import com.dbflow5.query.ModelQueriable;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Function;

/**
 * Description: A query-backed immutable [List]. Represents the results of a cursor without loading
 * the full query out into an actual [List]. Avoid keeping this class around without calling [close] as
 * it leaves a [FlowCursor] object active.
 */
public class FlowQueryList<T> implements List<T>, IFlowCursorIterator<T> {

    private static final EventHandler globalRefreshHandler = new EventHandler(EventRunner.getMainEventRunner());

    FlowCursorList<T> internalCursorList;
    private EventHandler refreshHandler;
    private boolean pendingRefresh;

    public FlowQueryList(FlowCursorList<T> internalCursorList, EventHandler refreshHandler) {
        this.internalCursorList = internalCursorList;
        this.refreshHandler = refreshHandler;
        if(refreshHandler == null) {
            this.refreshHandler = globalRefreshHandler;
        }
    }

    /**
     * A mutable list that does not reflect changes on the underlying DB.
     * @return a mutable list that does not reflect changes on the underlying DB.
     */
    public List<T> copy() {
        return internalCursorList.all();
    }

    RetrievalAdapter<T> retrievalAdapter() {
        return internalCursorList.instanceAdapter;
    }

    @Override
    public int size() {
        return (int)internalCursorList.count();
    }

    private final Runnable refreshRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized(this) {
                pendingRefresh = false;
            }
            refresh();
        }
    };

    public FlowQueryList(Builder<T> builder) {
        this(new FlowCursorList.Builder<>(builder.modelQueriable, builder.databaseWrapper)
                .cursor(builder.cursor)
                .build(), builder.refreshHandler);
    }

    public void addOnCursorRefreshListener(Function<FlowCursorList<T>, Void> onCursorRefreshListener) {
        internalCursorList.addOnCursorRefreshListener(onCursorRefreshListener);
    }

    public void removeOnCursorRefreshListener(Function<FlowCursorList<T>, Void> onCursorRefreshListener) {
        internalCursorList.removeOnCursorRefreshListener(onCursorRefreshListener);
    }

    public FlowCursorList<T> cursorList() {
        return internalCursorList;
    }

    /**
     * Constructs a new [Builder] that reuses the underlying [FlowCursor]
     * @return Constructs a new [Builder] that reuses the underlying [FlowCursor],
     * callbacks, and other properties.
     */
    public Builder<T> newBuilder() {
        return new Builder<>(internalCursorList, null);
    }

    /**
     * Refreshes the content backing this list.
     */
    public void refresh() {
        internalCursorList.refresh();
    }

    /**
     * Will refresh content at a slightly later time, and multiple subsequent calls to this method within
     * a short period of time will be combined into one call.
     */
    public void refreshAsync() {
        synchronized(this) {
            if (pendingRefresh) {
                return;
            }
            pendingRefresh = true;
        }
        refreshHandler.postTask(refreshRunnable);
    }

    /**
     * Checks to see if the table contains the object only if its a [T]
     *
     * @param element A model class. For interface purposes, this must be an Object.
     * @return always false if its anything other than the current table. True if [com.dbflow5.structure.Model.exists] passes.
     */
    @Override
    public boolean contains(Object element) {
        return internalCursorList.instanceAdapter.exists((T)element, internalCursorList.databaseWrapper);
    }

    /**
     * If the collection is null or empty, we return false.
     *
     * @param elements The collection to check if all exist within the table.
     * @return true if all items exist in table, false if at least one fails.
     */
    @Override
    public boolean containsAll(Collection<?> elements) {
        boolean contains = !elements.isEmpty();
        if (contains) {
            contains = elements.containsAll(this);
        }
        return contains;
    }

    /**
     * Returns the item from the backing [FlowCursorList]. First call
     * will load the model from the cursor, while subsequent calls will use the cache.
     *
     * @param index the row from the internal [FlowCursorList] query that we use.
     * @return A model converted from the internal [FlowCursorList]. For
     * performance improvements, ensure caching is turned on.
     */
    @Override
    public T get(long index) {
        return internalCursorList.get(index);
    }

    @Override
    public T get(int index) {
        return internalCursorList.get((long)index);
    }

    @Override
    public int indexOf(Object o) {
        throw new UnsupportedOperationException(
                "We cannot determine which index in the table this item exists at efficiently");
    }

    @Override
    public boolean isEmpty() {
        return internalCursorList.isEmpty();
    }

    /**
     * An iterator that loops through all items in the list.
     * @return An iterator that loops through all items in the list.
     * Be careful as this method will convert all data under this table into a list of [T] in the UI thread.
     */
    @Override
    public FlowCursorIterator<T> iterator() {
        return new FlowCursorIterator<>(internalCursorList.databaseWrapper, this);
    }

    @Override
    public FlowCursorIterator<T> iterator(long startingLocation, long limit) {
        return new FlowCursorIterator<>(internalCursorList.databaseWrapper, this, startingLocation, limit);
    }

    @Override
    public int lastIndexOf(Object o) {
        throw new UnsupportedOperationException(
                "We cannot determine which index in the table this item exists at efficiently");
    }

    /**
     * A list iterator that loops through all items in the list.
     * @return A list iterator that loops through all items in the list.
     * Be careful as this method will convert all data under this table into a list of [T] in the UI thread.
     */
    @Override
    public ListIterator<T> listIterator() {
        return new FlowCursorIterator<>(internalCursorList.databaseWrapper, this);
    }

    /**
     * A list iterator that loops through all items in the list.
     * @param index The index to start the iterator.
     * @return A list iterator that loops through all items in the list.
     * Be careful as this method will convert all data under this table into a list of [T] in the UI thread.
     */
    @Override
    public ListIterator<T> listIterator(int index) {
        return new FlowCursorIterator<>(internalCursorList.databaseWrapper, this, index, 0);
    }

    @Override
    public List<T> subList(int fromIndex, int toIndex) {
        List<T> tableList = internalCursorList.all();
        return tableList.subList(fromIndex, toIndex);
    }

    @Override
    public long count() {
        return internalCursorList.count();
    }

    @Override
    public FlowCursor cursor() {
        return internalCursorList.cursor();
    }

    @Override
    public boolean trackingCursor() {
        return internalCursorList.trackingCursor();
    }

    @Override
    public boolean isClosed() {
        return internalCursorList.isClosed();
    }

    @Override
    public void close() {
        internalCursorList.close();
    }

    @Override
    public Object[] toArray() {
        return new Object[0];
    }

    @Override
    public <T1> T1[] toArray(T1[] t1s) {
        return null;
    }

    @Override
    public boolean add(T t) {
        return false;
    }

    @Override
    public boolean remove(Object o) {
        return false;
    }

    @Override
    public boolean addAll(Collection<? extends T> collection) {
        return false;
    }

    @Override
    public boolean addAll(int i, Collection<? extends T> collection) {
        return false;
    }

    @Override
    public boolean removeAll(Collection<?> collection) {
        return false;
    }

    @Override
    public boolean retainAll(Collection<?> collection) {
        return false;
    }

    @Override
    public void clear() {

    }

    @Override
    public boolean equals(Object o) {
        return false;
    }

    @Override
    public int hashCode() {
        return 0;
    }

    @Override
    public T set(int i, T t) {
        return null;
    }

    @Override
    public void add(int i, T t) {

    }

    @Override
    public T remove(int i) {
        return null;
    }

    public static class Builder<T> {
        Class<T> table;

        FlowCursor cursor = null;
        ModelQueriable<T> modelQueriable;
        DatabaseWrapper databaseWrapper;
        EventHandler refreshHandler;

        public Builder(FlowCursorList<T> cursorList, EventHandler refreshHandler) {
            this.databaseWrapper = cursorList.databaseWrapper;
            table = cursorList.table;
            cursor = cursorList.cursor();
            modelQueriable = cursorList.modelQueriable;
            this.refreshHandler = refreshHandler;
            if(this.refreshHandler == null){
                this.refreshHandler = globalRefreshHandler;
            }
        }

        public Builder(ModelQueriable<T> modelQueriable, DatabaseWrapper databaseWrapper, EventHandler refreshHandler) {
            this.databaseWrapper = databaseWrapper;
            this.table = modelQueriable.table();
            this.modelQueriable = modelQueriable;
            this.refreshHandler = refreshHandler;
            if(this.refreshHandler == null){
                this.refreshHandler = globalRefreshHandler;
            }
        }

        Builder<T> cursor(FlowCursor cursor) {
            this.cursor = cursor;
            return this;
        }

        public FlowQueryList<T> build() {
            return new FlowQueryList<>(this);
        }
    }

}
